﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace KAImage.Algorithms
{
    public class AutoAdjusting : IDisposable
    {
        private string IniFileName = "AutoADOption.ini";

        private Bitmap _source = null;

        private Bitmap _image = null;

        // 计算直方图 RGB CHANNEL
        private int[] histB = new int[256];
        private int[] histG = new int[256];
        private int[] histR = new int[256];

        private int maxB = 0;
        private int maxG = 0;
        private int maxR = 0;
        private int minB = 0;
        private int minG = 0;
        private int minR = 0;
        private int minV = 0;
        private int maxV = 0;

        private double _lowCut = 0.1;
        public double LowCut
        {
            get{ return _lowCut; }
            set { _lowCut = value; }
        }

        private double _highCut = 0.1;
        public double HighCut
        {
            get { return _highCut; }
            set { _highCut = value; }
        }

        public AutoAdjusting()
        {
            LoadConfig(); 
        }
     
        public AutoAdjusting(Bitmap bmp)
        {
            LoadConfig();
            _source = bmp;
            GetHistogram();
        }

        public void LoadConfig()
        {
            string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase).Substring(6);
            IniFileName = Path.Combine(directory, "AutoADOption.ini");
            string lowCut = IniConfigFile.IniValue("config", "LowCut", IniFileName);
            string highCut = IniConfigFile.IniValue("config", "HighCut", IniFileName);
            _lowCut = Convert.ToDouble(lowCut);
            _highCut = Convert.ToDouble(highCut);
        }

        public void SaveConfig(double low, double high)
        {
            _lowCut = low;
            _highCut = high;
            IniConfigFile.WriteValue("config", "LowCut", _lowCut.ToString(), IniFileName);
            IniConfigFile.WriteValue("config", "HighCut", _highCut.ToString(), IniFileName);
        }

        private void ResetData()
        {
            for (int i = 0; i < 256; i++)
            {
                histB[i] = 0;
                histG[i] = 0;
                histR[i] = 0;
            }
        }

        private void GetHistogram()
        {
            ResetData();
            ImageHistograms.GetHistogramRGBChannel(_source, histR, histG, histB);
            int pixelCnt = _source.Width * _source.Height;
            int sum = 0;

            //根据lowCut跟highCut计算各个通道的最大值最小值。
            for (int i = 0; i < 256; i++)
            {
                sum += histB[i];
                if (sum >= pixelCnt * _lowCut / 100)
                {
                    minB = i; break;
                }
            }
            sum = 0;
            for (int i = 255; i >= 0; i--)
            {
                sum += histB[i];
                if (sum >= pixelCnt * _highCut / 100)
                {
                    maxB = i; break;
                }
            }
            sum = 0;
            for (int i = 0; i < 256; i++)
            {
                sum += histG[i];
                if (sum >= pixelCnt * _lowCut / 100)
                {
                    minG = i; break;
                }
            }
            sum = 0;
            for (int i = 255; i >= 0; i--)
            {
                sum += histG[i];
                if (sum >= pixelCnt * _highCut / 100)
                {
                    maxG = i; break;
                }
            }
            sum = 0;
            for (int i = 0; i < 256; i++)
            {
                sum += histR[i];
                if (sum >= pixelCnt * _lowCut / 100)
                {
                    minR = i; break;
                }
            }
            sum = 0;
            for (int i = 255; i >= 0; i--)
            {
                sum += histR[i];
                if (sum >= pixelCnt * _highCut / 100)
                {
                    maxR = i; break;
                }
            }
            // 获取三通道中的最大值最小值
            minV = minB;
            if (minV > minG)
            {
                minV = minG;
            }
            if (minV > minR)
            {
                minV = minR;
            }
            maxV = maxB;
            if (maxV < maxG)
            {
                maxV = maxG;
            }
            if (maxV < maxR)
            {
                maxV = maxR;
            }
        }

        public Bitmap AutoContrast()
        {
            Dispose();
            Bitmap image = _source.Clone() as Bitmap;
            int[] map = MakeMapping(minV, maxV);
            ImageAdjustments.ApplyMappingToRGB(image, map);
            return image;
        }

        public Bitmap AutoLevel()
        {
            Dispose();
            Bitmap image = _source.Clone() as Bitmap;
            int[] mapB = MakeMapping(minB, maxB);
            int[] mapG = MakeMapping(minG, maxG);
            int[] mapR = MakeMapping(minR, maxR);
            ImageAdjustments.ApplyMappingToRGB(image, mapR, mapG, mapB);
            return image;
        }

        /// <summary>
        /// 线性映射颜色表
        /// </summary>
        /// <param name="minV"></param>
        /// <param name="maxV"></param>
        /// <returns></returns>
        private int[] MakeMapping(int minV, int maxV)
        {
            //构建映射表
            int[] map = new int[256];
            for (int i = 0; i < 256; i++)
            {
                map[i] = i;
            }
            if(minV < 0 || maxV > 255 || maxV == minV)
            {
                return map;
            }
            for (int i = 0; i < 256; i++)
            {
                if (i < minV)
                {
                    map[i] = 0;
                }
                else if (i > maxV)
                {
                    map[i] = 255;
                }
                else
                {
                    map[i] = (int)(((double)(i - minV) / (double)(maxV - minV)) * 255);
                }
            }
            return map;
        }

        public void Dispose()
        {
           if(_image != null)
            {
                _image.Dispose();
                _image = null;
            }
        }
      
    }
}
